import os
import urllib.parse
import secrets
from fastapi import FastAPI, Request, Depends, HTTPException, Form, Cookie, Response, status
from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from sqlalchemy import create_engine, Column, Integer, String, Float, Boolean, Text, DateTime, MetaData, Table, select, update, delete, insert, func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session
from typing import List, Optional, Dict, Any
from datetime import datetime, timedelta, timezone
import pathlib
import json
from pydantic import BaseModel
import logging
import hashlib
import base64
import pytz
from functools import wraps
from typing import List, Optional
logging.basicConfig(level=logging.DEBUG, filename="uvicorn.log")
logger = logging.getLogger(__name__)

# 全局变量存储当前数据库引擎
_current_engine = None

# 数据库配置模型
class DatabaseConfig(BaseModel):
    db_type: str  # "sqlite" 或 "mysql"
    # SQLite 专用配置
    sqlite_path: Optional[str] = None
    # MySQL 专用配置
    mysql_host: Optional[str] = None
    mysql_port: Optional[int] = None
    mysql_user: Optional[str] = None
    mysql_password: Optional[str] = None
    mysql_db_name: Optional[str] = None

# 默认数据库配置
# 设置SQLite路径为当前目录下的trade_monitor.db
current_dir = os.path.dirname(os.path.abspath(__file__))
DB_CONFIG = {
    "db_type": "mysql",
    # SQLite 配置 - 使用当前目录下的trade_monitor.db
    "sqlite_path": os.path.join(current_dir, "trade_monitor.db"),
    # MySQL 配置
    "mysql_host": "192.168.127.12",
    "mysql_port": 3306,
    "mysql_user": "remote_admin",
    "mysql_password": "alibaba999",
    "mysql_db_name": "trade_monitor"
}

# 加载数据库配置
CONFIG_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "db_config.json")
if os.path.exists(CONFIG_FILE):
    with open(CONFIG_FILE, "r", encoding="utf-8") as f:
        new_config = json.load(f)
        
        # 迁移旧配置键到新键
        if 'db_host' in new_config:
            new_config['mysql_host'] = new_config.pop('db_host')
        if 'db_port' in new_config:
            new_config['mysql_port'] = new_config.pop('db_port')
        if 'db_user' in new_config:
            new_config['mysql_user'] = new_config.pop('db_user')
        if 'db_password' in new_config:
            new_config['mysql_password'] = new_config.pop('db_password')
        if 'db_name' in new_config:
            new_config['mysql_db_name'] = new_config.pop('db_name')
        if 'db_path' in new_config:
            new_config['sqlite_path'] = new_config.pop('db_path')
        
        # 检查配置是否发生变化
        if (new_config.get("db_type") != DB_CONFIG.get("db_type") or
            new_config.get("mysql_host") != DB_CONFIG.get("mysql_host") or
            new_config.get("mysql_port") != DB_CONFIG.get("mysql_port") or
            new_config.get("mysql_user") != DB_CONFIG.get("mysql_user") or
            new_config.get("mysql_password") != DB_CONFIG.get("mysql_password") or
            new_config.get("mysql_db_name") != DB_CONFIG.get("mysql_db_name") or
            new_config.get("sqlite_path") != DB_CONFIG.get("sqlite_path")):
            # 强制下次get_db_engine调用创建新连接
            if _current_engine is not None:
                _current_engine.dispose()
                _current_engine = None
                logger.debug(f"Disposed database connection due to config change. Old type: {DB_CONFIG['db_type']}, New type: {new_config.get('db_type')}")
            # 立即更新配置，避免后续逻辑依赖旧配置
            DB_CONFIG.update(new_config)

# 创建数据库连接
def get_db_engine():
    global _current_engine
    try:
        # 每次调用时重新加载配置
        if os.path.exists(CONFIG_FILE):
            with open(CONFIG_FILE, "r") as f:
                new_config = json.load(f)
                # 迁移旧配置键到新键
                if 'db_host' in new_config:
                    new_config['mysql_host'] = new_config.pop('db_host')
                if 'db_port' in new_config:
                    new_config['mysql_port'] = new_config.pop('db_port')
                if 'db_user' in new_config:
                    new_config['mysql_user'] = new_config.pop('db_user')
                if 'db_password' in new_config:
                    new_config['mysql_password'] = new_config.pop('db_password')
                if 'db_name' in new_config:
                    new_config['mysql_db_name'] = new_config.pop('db_name')
                if 'db_path' in new_config:
                    new_config['sqlite_path'] = new_config.pop('db_path')
                
                # 更新全局配置
                DB_CONFIG.update(new_config)
                logger.debug("Reloaded database configuration from db_config.json")
        
        # 如果已有连接且数据库类型未改变，直接返回现有引擎
        if _current_engine is not None and _current_engine.url.drivername == DB_CONFIG["db_type"]:
            return _current_engine
            
        # 关闭现有连接（如果存在）
        if _current_engine is not None:
            _current_engine.dispose()
            logger.debug("Disposed previous database connection")
            
        # 创建新连接
        if DB_CONFIG["db_type"] == "sqlite":
            # 移除路径自动覆盖逻辑
            # if not os.path.exists(DB_CONFIG["sqlite_path"]):
            #         DB_CONFIG["sqlite_path"] = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 
            #    "trade_monitor.db")
            SQLALCHEMY_DATABASE_URL = f"sqlite:///{DB_CONFIG['sqlite_path']}"
            logger.debug(f"Connecting to SQLite database: {SQLALCHEMY_DATABASE_URL}")
            _current_engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
        elif DB_CONFIG["db_type"] == "mysql":
            # 确保 MySQL 驱动已安装
            try:
                import pymysql
                # 强制密码使用UTF-8编码
                connection_string = f"mysql+pymysql://{DB_CONFIG['mysql_user']}:{urllib.parse.quote_plus(DB_CONFIG['mysql_password'])}@{DB_CONFIG['mysql_host']}:{DB_CONFIG['mysql_port']}/{DB_CONFIG['mysql_db_name']}"
                
                # 测试MySQL连接
                try:
                    _current_engine = create_engine(connection_string)
                    # 测试连接
                    with _current_engine.connect(): pass
                    return _current_engine
                except Exception as e:
                    logger.error(f"MySQL connection failed: {str(e)}")
                    raise HTTPException(status_code=500, detail=f"MySQL connection failed: {str(e)}")
            except Exception as e:
                logger.error(f"MySQL connection setup failed: {str(e)}")
                # 自动回退到SQLite数据库
                DB_CONFIG["db_type"] = "sqlite"
                # 确保使用当前目录下的trade_monitor.db
                current_dir = os.path.dirname(os.path.abspath(__file__))
                DB_CONFIG["sqlite_path"] = os.path.join(current_dir, "trade_monitor.db")
                SQLALCHEMY_DATABASE_URL = f"sqlite:///{DB_CONFIG['sqlite_path']}"
                logger.debug(f"Fallback to SQLite database: {SQLALCHEMY_DATABASE_URL}")
                _current_engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
                
                # 记录错误信息
                DB_CONFIG["last_error"] = {
                    "message": "MySQL数据库连接失败，已自动切换到SQLite数据库",
                    "error": str(e),
                    "suggestion": "请检查MySQL配置（密码不能包含中文等非ASCII字符）"
                }

                return _current_engine
        else:
            raise ValueError("Unsupported database type")
        return _current_engine
    except Exception as e:
        logger.error(f"Failed to create database engine: {str(e)}", exc_info=True)
        # 自动回退到SQLite数据库
        DB_CONFIG["db_type"] = "sqlite"
        # 确保使用当前目录下的trade_monitor.db
        current_dir = os.path.dirname(os.path.abspath(__file__))
        DB_CONFIG["sqlite_path"] = os.path.join(current_dir, "trade_monitor.db")
        SQLALCHEMY_DATABASE_URL = f"sqlite:///{DB_CONFIG['sqlite_path']}"
        logger.debug(f"Fallback to SQLite database: {SQLALCHEMY_DATABASE_URL}")
        _current_engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
        
        # 记录错误信息
        DB_CONFIG["last_error"] = {
            "message": "数据库连接失败，已自动切换到SQLite数据库",
            "error": str(e),
            "suggestion": "请检查数据库配置或联系管理员"
        }
        
        return _current_engine

engine = get_db_engine()
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()

# 定义数据库模型
class Product(Base):
    __tablename__ = "products"
    
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    exchange_code = Column(String)
    margin = Column(Float)
    stoploss = Column(Float)
    takeprofit = Column(Float)
    limit_stoploss = Column(Float)
    mtp = Column(String)
    dks = Column(String)
    is_monitored = Column(Integer)
    add_time = Column(String)

class Config(Base):
    __tablename__ = "config"
    
    id = Column(Integer, primary_key=True, index=True)
    key = Column(String, index=True)
    value = Column(String)
    description = Column(String)
    updated_at = Column(DateTime)
    
    @staticmethod
    def get_theme(db: Session):
        theme = db.query(Config).filter(Config.key == "theme").first()
        return theme.value if theme else "light"
    
    @staticmethod
    def set_theme(db: Session, theme: str):
        theme_config = db.query(Config).filter(Config.key == "theme").first()
        if not theme_config:
            theme_config = Config(
                key="theme",
                value=theme,
                description="Application theme (light/dark)",
                updated_at=datetime.now()
            )

class User(Base):
    __tablename__ = "users"
    
    id = Column(Integer, primary_key=True, index=True)
    username = Column(String(50), unique=True, index=True)
    password_hash = Column(String(128))
    salt = Column(String(32))
    is_active = Column(Boolean, default=True)
    is_admin = Column(Boolean, default=False)
    user_type = Column(String(20), default='viewer')  # 用户类型：super_admin, admin, operator, viewer, visitor
    session_duration = Column(Integer, default=360)  # 默认6小时，单位分钟
    last_login = Column(DateTime, nullable=True)
    created_at = Column(DateTime, default=datetime.now)
    updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
    
    @staticmethod
    def hash_password(password: str, salt: str = None) -> tuple:
        """对密码进行加盐哈希"""
        if not salt:
            salt = secrets.token_hex(16)  # 生成32字符的随机盐
        
        # 组合密码和盐，然后进行SHA-256哈希
        password_bytes = (password + salt).encode('utf-8')
        hash_obj = hashlib.sha256(password_bytes)
        password_hash = hash_obj.hexdigest()
        
        return password_hash, salt
    
    @staticmethod
    def verify_password(plain_password: str, hashed_password: str, salt: str) -> bool:
        """验证密码是否正确"""
        password_hash, _ = User.hash_password(plain_password, salt)
        return password_hash == hashed_password

# 权限检查装饰器和函数
def require_permission(required_permissions: List[str]):
    """权限检查装饰器"""
    def decorator(func):
        @wraps(func)
        async def wrapper(*args, **kwargs):
            request = kwargs.get('request')
            if not request:
                for arg in args:
                    if isinstance(arg, Request):
                        request = arg
                        break
            
            if not request:
                raise HTTPException(status_code=403, detail="无法获取请求信息")
            
            # 获取当前用户
            user = getattr(request.state, 'user', None)
            if not user:
                raise HTTPException(status_code=401, detail="用户未登录")
            
            # 检查权限
            user_permissions = get_user_permissions(user)
            
            # 检查是否有足够的权限
            has_permission = any(perm in user_permissions for perm in required_permissions)
            
            if not has_permission:
                raise HTTPException(status_code=403, detail="权限不足")
            
            return await func(*args, **kwargs)
        return wrapper
    return decorator

def get_user_permissions(user) -> List[str]:
    """根据用户类型获取权限列表"""
    permissions = {
        'super_admin': [
            'user_manage', 'product_manage', 'trade_manage', 'config_manage', 
            'data_export', 'system_admin'
        ],
        'admin': [
            'user_manage', 'product_manage', 'trade_manage', 'config_view', 
            'data_export'
        ],
        'operator': [
            'product_view', 'trade_view', 'data_export'
        ],
        'viewer': [
            'product_view', 'trade_view'
        ],
        'visitor': [
            'product_view'
        ]
    }
    
    # 如果用户类型不存在，默认为viewer
    user_type = getattr(user, 'user_type', 'viewer')
    return permissions.get(user_type, permissions['viewer'])

def check_permission(user, required_permission: str) -> bool:
    """检查用户是否有特定权限"""
    user_permissions = get_user_permissions(user)
    return required_permission in user_permissions

# 用户登录会话模型
class UserSession(Base):
    __tablename__ = "user_sessions"
    
    id = Column(Integer, primary_key=True, index=True)
    user_id = Column(Integer, index=True)
    session_token = Column(String(64), unique=True, index=True)
    expires_at = Column(DateTime)
    created_at = Column(DateTime, default=datetime.now)
    
    @staticmethod
    def create_session(user_id: int, duration_minutes: int = 360) -> str:
        """创建新的会话令牌"""
        token = secrets.token_hex(32)  # 生成64字符的随机令牌
        expires_at = datetime.now(timezone.utc) + timedelta(minutes=duration_minutes)
        return token, expires_at

    @staticmethod
    def set_theme(db: Session, theme: str):
        theme_config = db.query(Config).filter(Config.key == "theme").first()
        if not theme_config:
            theme_config = Config(
                key="theme",
                value=theme,
                description="Application theme (light/dark)",
                updated_at=datetime.now()
            )
            db.add(theme_config)
        else:
            theme_config.value = theme
            theme_config.updated_at = datetime.now()
        db.commit()

class TradeRecord(Base):
    __tablename__ = "trade_records"
    
    id = Column(Integer, primary_key=True, index=True)
    product_name = Column(String)
    direction = Column(String)
    offset = Column(String)
    volume = Column(Float)
    price = Column(Float)
    reason = Column(String)
    timestamp = Column(String)
    created_at = Column(DateTime)

# 依赖项
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

# 创建FastAPI应用
app = FastAPI(title="交易监控系统", debug=True)

# 全局异常处理器
@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
    # 如果是数据库连接错误
    if isinstance(exc.detail, dict) and exc.detail.get("message") == "MySQL数据库连接失败":
        # 记录连接错误到配置中，以便在配置页面显示
        DB_CONFIG["connection_error"] = {
            "type": "mysql",
            "message": exc.detail.get("error", "连接失败")
        }
        
        return templates.TemplateResponse(
            "error.html",
            {
                "request": request,
                "title": "数据库连接失败",
                "message": "无法连接到MySQL数据库，您可以使用数据库配置向导来切换到SQLite数据库",
                "error_details": exc.detail.get("error", ""),
                "show_config_button": True,
                "show_sqlite_button": True
            },
            status_code=exc.status_code
        )
    
    # 其他HTTP异常
    return templates.TemplateResponse(
        "error.html",
        {
            "request": request,
            "title": "发生错误",
            "message": str(exc.detail),
            "error_details": "",
            "show_config_button": False,
            "show_sqlite_button": False
        },
        status_code=exc.status_code
    )

import pymysql

@app.exception_handler(Exception)
async def general_exception_handler(request: Request, exc: Exception):
    logger.error(f"Unhandled exception: {str(exc)}", exc_info=True)
    
    # 检查是否是数据库连接异常
    is_db_exception = False
    error_details = str(exc)
    
    # 检查是否是MySQL连接错误
    if isinstance(exc, (pymysql.err.OperationalError, pymysql.err.MySQLError)):
        is_db_exception = True
    # 检查是否是SQLAlchemy的OperationalError
    elif hasattr(exc, '__cause__') and isinstance(exc.__cause__, (pymysql.err.OperationalError, pymysql.err.MySQLError)):
        is_db_exception = True
    # 检查错误信息是否包含数据库连接相关关键字
    elif 'database' in error_details.lower() or 'db' in error_details.lower() or 'sql' in error_details.lower() or 'connection' in error_details.lower():
        is_db_exception = True
    
    if is_db_exception:
        # 记录连接错误到配置中，以便在配置页面显示
        DB_CONFIG["connection_error"] = {
            "type": DB_CONFIG.get("db_type", "mysql"),
            "message": error_details
        }
        
        # 数据库连接异常，显示配置按钮和切换到SQLite的按钮
        return templates.TemplateResponse(
            "error.html",
            {
                "request": request,
                "title": "数据库连接错误",
                "message": "无法连接到数据库，请使用数据库配置向导来修改配置或切换到SQLite数据库",
                "error_details": error_details,
                "show_config_button": True,
                "show_sqlite_button": True
            },
            status_code=500
        )
    
    # 其他异常
    return templates.TemplateResponse(
        "error.html",
        {
            "request": request,
            "title": "系统错误",
            "message": "系统发生意外错误，请稍后再试",
            "error_details": error_details,
            "show_config_button": False,
            "show_sqlite_button": False
        },
        status_code=500
    )

import logging
logging.basicConfig(level=logging.DEBUG, filename="uvicorn.log")
logger = logging.getLogger(__name__)

@app.on_event("startup")
async def startup_event():
    logger.info("Starting FastAPI application")
    try:
        # 测试数据库连接
        engine = get_db_engine()
        logger.info("Database connection successful")
        
        # 创建数据库表
        try:
            Base.metadata.create_all(bind=engine)
            logger.info("Database tables created successfully")
        except Exception as table_error:
            logger.error(f"Failed to create database tables: {str(table_error)}", exc_info=True)
            logger.warning("Application will continue running, but some features may not work properly")
        
        # 初始化管理员账户
        try:
            db = SessionLocal()
            try:
                # 检查是否已存在管理员账户
                admin = db.query(User).filter(User.username == "admin").first()
                if not admin:
                    # 创建默认管理员账户
                    password_hash, salt = User.hash_password("admin123")
                    admin = User(
                        username="admin",
                        password_hash=password_hash,
                        salt=salt,
                        is_active=True,
                        is_admin=True,
                        session_duration=360,  # 6小时
                        created_at=datetime.now(),
                        updated_at=datetime.now()
                    )
                    db.add(admin)
                    db.commit()
                    logger.info("Created default admin account")
            except Exception as admin_error:
                logger.error(f"Failed to initialize admin account: {str(admin_error)}", exc_info=True)
                logger.warning("Admin account initialization failed, but application will continue running")
            finally:
                db.close()
        except Exception as session_error:
            logger.error(f"Failed to create database session: {str(session_error)}", exc_info=True)
            logger.warning("Database session creation failed, but application will continue running")
    except Exception as e:
        logger.error(f"Database connection failed: {str(e)}", exc_info=True)
        # 不再直接抛出异常，允许应用继续运行，这样用户可以看到友好的错误页面
        logger.warning("Application will continue running despite database connection failure")
        
    # 确保数据库配置路由可用
    logger.info("Ensuring database configuration routes are available")

# 获取数据库会话
def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

# 验证用户会话
async def get_current_user(
    request: Request,
    session_token: Optional[str] = Cookie(None),
    db: Session = Depends(get_db)
):
    # 如果是登录页面或静态资源，不需要验证
    if request.url.path == "/login" or request.url.path.startswith("/static"):
        return None
        
    if not session_token:
        return None
        
    # 查询会话
    session = db.query(UserSession).filter(
        UserSession.session_token == session_token,
        UserSession.expires_at > datetime.now(timezone.utc)
    ).first()
    
    if not session:
        return None
        
    # 获取用户
    user = db.query(User).filter(User.id == session.user_id, User.is_active == True).first()
    if not user:
        return None
        
    return user

@app.middleware("http")
async def auth_middleware(request: Request, call_next):
    # 增强版全局请求日志
    logger.debug(f"===== NEW REQUEST =====")
    logger.debug(f"Method: {request.method}")
    logger.debug(f"URL: {request.url}")
    logger.debug(f"Path: {request.url.path}")
    
    # 不需要验证的路径
    public_paths = [
        "/login", 
        "/static", 
        "/favicon.ico",
        "/trade_records"
    ]
    
    # 检查是否是公开路径
    is_public = any(request.url.path.startswith(path) for path in public_paths) or \
                any(path in request.url.path for path in public_paths)
    
    # 初始化用户状态
    request.state.user = None
    request.state.is_admin = False
    
    # 如果是公开路径，添加基本的请求属性以支持模板渲染
    if is_public:
        # 为模板渲染提供基本的url_for功能
        def simple_url_for(name, **path_params):
            # 处理常见的路由名称
            if name == 'static':
                path = path_params.get('path', '')
                return f"/static/{path}"
            elif name == 'login':
                return "/login"
            elif name == 'trade_records':
                return "/trade_records"
            else:
                # 对于其他路由，返回基本路径
                return f"/{name}"
        
        request.url_for = simple_url_for
    
    if not is_public:
        # 获取会话令牌
        session_token = request.cookies.get("session_token")
        
        if not session_token:
            # 没有会话令牌，重定向到登录页面
            logger.debug("No session token, redirecting to login")
            return RedirectResponse(url="/login", status_code=303)
        
        try:
            # 验证会话
            db = SessionLocal()
            try:
                session = db.query(UserSession).filter(
                    UserSession.session_token == session_token,
                    UserSession.expires_at > datetime.utcnow()
                ).first()
                
                if session:
                    # 获取用户信息
                    user = db.query(User).filter(User.id == session.user_id, User.is_active == True).first()
                    if user:
                        # 设置用户状态
                        request.state.user = user
                        request.state.is_admin = user.is_admin
                        logger.debug(f"User authenticated: {user.username}, is_admin: {user.is_admin}")
                    else:
                        logger.debug("Invalid session: user not found or inactive")
                else:
                    logger.debug("Invalid or expired session token")
                
                if not session:
                    # 会话无效或过期，重定向到登录页面
                    logger.debug("Invalid or expired session, redirecting to login")
                    response = RedirectResponse(url="/login", status_code=303)
                    response.delete_cookie(key="session_token")
                    # 确保清理所有可能的数据库配置信息
                    response.delete_cookie("db_path")
                    response.delete_cookie("db_name")
                    response.delete_cookie("db_password")
                    response.delete_cookie("db_config_leak")
                    return response
                    
                # 获取用户
                user = db.query(User).filter(User.id == session.user_id, User.is_active == True).first()
                if not user:
                    # 用户不存在或已禁用，重定向到登录页面
                    logger.debug("User not found or inactive, redirecting to login")
                    response = RedirectResponse(url="/login", status_code=303)
                    response.delete_cookie(key="session_token")
                    # 确保清理所有可能的数据库配置信息
                    response.delete_cookie("db_path")
                    response.delete_cookie("db_name")
                    response.delete_cookie("db_password")
                    response.delete_cookie("db_config_leak")
                    return response
                    
                # 将用户信息添加到请求状态
                request.state.user = user
                request.state.user_id = user.id
                request.state.username = user.username
                request.state.is_admin = user.is_admin
                request.state.user_type = user.user_type
            finally:
                db.close()
        except Exception as e:
            # 如果数据库连接失败，但请求是针对数据库配置页面的，则允许访问
            if "/db_config_editor" in request.url.path or "/save_db_config" in request.url.path or "/test_db_connection" in request.url.path:
                logger.warning(f"Database connection failed but allowing access to DB config page: {str(e)}")
                # 继续处理请求
            else:
                # 其他页面重定向到登录
                logger.error(f"Session validation failed: {str(e)}", exc_info=True)
                response = RedirectResponse(url="/login", status_code=303)
                # 确保清理所有可能的数据库配置信息
                response.delete_cookie("db_path")
                response.delete_cookie("db_name")
                response.delete_cookie("db_password")
                response.delete_cookie("db_config_leak")
                return response
    
    try:
        response = await call_next(request)
        logger.debug(f"Response status: {response.status_code}")
    except Exception as e:
        logger.error(f"Request failed: {str(e)}", exc_info=True)
        # 如果是数据库配置相关的路由，尝试提供友好的错误页面
        if "/db_config_editor" in request.url.path or "/save_db_config" in request.url.path or "/test_db_connection" in request.url.path:
            from fastapi.responses import HTMLResponse
            error_html = f"""
            <!DOCTYPE html>
            <html>
            <head>
                <title>数据库配置错误</title>
                <style>
                    body {{ font-family: Arial, sans-serif; margin: 40px; }}
                    .error-container {{ border: 1px solid #f44336; padding: 20px; border-radius: 5px; }}
                    .error-title {{ color: #f44336; }}
                    .error-message {{ margin-top: 20px; }}
                    .button {{ background-color: #4CAF50; color: white; padding: 10px 15px; 
                              border: none; border-radius: 4px; cursor: pointer; margin-top: 20px; }}
                </style>
            </head>
            <body>
                <div class="error-container">
                    <h1 class="error-title">数据库配置错误</h1>
                    <div class="error-message">
                        <p>处理请求时发生错误: {str(e)}</p>
                        <p>请检查数据库配置并重试。</p>
                    </div>
                    <a href="/db_config_editor" class="button">返回配置页面</a>
                </div>
            </body>
            </html>
            """
            return HTMLResponse(content=error_html, status_code=500)
        raise
    logger.debug(f"========================")
    return response

# 设置静态文件和模板
current_dir = os.path.dirname(os.path.abspath(__file__))
app.mount("/static", StaticFiles(directory=os.path.join(current_dir, "static")), name="static")
# 明确指定模板编码为UTF-8，解决中文显示乱码问题
templates = Jinja2Templates(
    directory=os.path.join(current_dir, "templates"),
    autoescape=True,
    enable_async=False  # 禁用异步渲染以避免事件循环冲突
)

# 添加 AuthenticationMiddleware
from starlette.middleware.authentication import AuthenticationMiddleware
from starlette.authentication import AuthCredentials, AuthenticationBackend, AuthenticationError

class YourAuthBackend(AuthenticationBackend):
    async def authenticate(self, request):
        session_token = request.cookies.get("session_token")
        if not session_token:
            return None
        
        db = SessionLocal()
        try:
            session = db.query(UserSession).filter(
                UserSession.session_token == session_token,
                UserSession.expires_at > datetime.utcnow()
            ).first()
            
            if session:
                user = db.query(User).filter(User.id == session.user_id).first()
                if user:
                    return AuthCredentials(["authenticated"]), user
        finally:
            db.close()
        
        return None

app.add_middleware(AuthenticationMiddleware, backend=YourAuthBackend())

# 用户登录相关路由
@app.get("/login", response_class=HTMLResponse)
async def login_page(request: Request, error: str = None):
    # 清理所有可能的数据库配置信息Cookie
    response = templates.TemplateResponse(
        "login.html", 
        {"request": request, "error": error, "theme": "light"}
    )
    
    # 无论是否已登录，都清理所有可能的数据库配置信息Cookie
    response.delete_cookie("db_path")
    response.delete_cookie("db_name")
    response.delete_cookie("db_password")
    response.delete_cookie("db_config_leak")
    
    # 检查是否已经登录
    session_token = request.cookies.get("session_token")
    if session_token:
        db = SessionLocal()
        try:
            session = db.query(UserSession).filter(
                UserSession.session_token == session_token,
                UserSession.expires_at > datetime.now(timezone.utc)
            ).first()
            
            if session:
                # 已登录，重定向到首页
                redirect_response = RedirectResponse(url="/", status_code=303)
                # 确保清理数据库配置信息
                redirect_response.delete_cookie("db_path")
                redirect_response.delete_cookie("db_name")
                redirect_response.delete_cookie("db_password")
                redirect_response.delete_cookie("db_config_leak")
                return redirect_response
        finally:
            db.close()
    
    return response

@app.post("/login")
async def login(
    response: Response,
    username: str = Form(...),
    password: str = Form(...),
    db: Session = Depends(get_db)
):
    # 查询用户
    user = db.query(User).filter(User.username == username).first()
    
    # 验证用户和密码
    if not user or not User.verify_password(password, user.password_hash, user.salt):
        return RedirectResponse(
            url=f"/login?error=用户名或密码错误", 
            status_code=303
        )
    
    if not user.is_active:
        return RedirectResponse(
            url=f"/login?error=账户已被禁用", 
            status_code=303
        )
    
    # 生成 session_token
    session_token = secrets.token_hex(32)
    # 生成UTC时间并手动格式化为GMT字符串以避免usegmt错误
    expires_at = datetime.utcnow() + timedelta(minutes=user.session_duration)
    # 格式化为HTTP cookie标准的GMT时间字符串
    expires_str = expires_at.strftime("%a, %d %b %Y %H:%M:%S GMT")

    # 创建或更新会话
    session = db.query(UserSession).filter(UserSession.user_id == user.id).first()
    if session:
        session.session_token = session_token
        session.expires_at = expires_at
    else:
        session = UserSession(
            user_id=user.id,
            session_token=session_token,
            expires_at=expires_at
        )
        db.add(session)

    db.commit()

    # 更新用户最后登录时间
    user.last_login = datetime.utcnow()
    db.commit()

    # 设置 cookie 并重定向到首页
    response = RedirectResponse(url="/", status_code=303)

    response.set_cookie(
        key="session_token",
        value=session_token,
        expires=expires_str,
        httponly=True,
        secure=False,
        samesite="lax",
        path='/'
    )
    
    # 确保会话已保存
    db.refresh(session)
    return response

@app.get("/logout")
async def logout(
    response: Response,
    session_token: Optional[str] = Cookie(None),
    db: Session = Depends(get_db)
):
    if session_token:
        # 删除会话
        db.query(UserSession).filter(UserSession.session_token == session_token).delete()
        db.commit()
    
    # 清除Cookie
    response = RedirectResponse(url="/login", status_code=303)
    response.delete_cookie(key="session_token")
    
    return response

@app.get("/user/settings", response_class=HTMLResponse)
async def user_settings(
    request: Request,
    error: str = None,
    success: bool = False,
    db: Session = Depends(get_db)
):
    # 获取当前用户
    user = getattr(request.state, "user", None)
    if not user:
        return RedirectResponse(url="/login", status_code=303)
    
    try:
        theme = Config.get_theme(db)
    except Exception:
        theme = "light"
    
    return templates.TemplateResponse(
        "user_settings.html",
        {
            "request": request,
            "user": user,
            "theme": theme,
            "error": error,
            "success": success
        }
    )

@app.post("/user/change_password")
async def change_password(
    request: Request,
    current_password: str = Form(...),
    new_password: str = Form(...),
    confirm_password: str = Form(...),
    db: Session = Depends(get_db)
):
    # 记录开始修改密码的请求
    logger.info("===== 开始处理密码修改请求 =====")
    
    # 获取当前用户信息
    user_from_state = getattr(request.state, "user", None)
    if not user_from_state:
        logger.warning("密码修改请求: 未找到用户会话，重定向到登录")
        return RedirectResponse(url="/login", status_code=303)
    
    # 重新在当前会话中查询用户，解决"not persistent within this Session"错误
    user = db.query(User).filter(User.id == user_from_state.id).first()
    if not user:
        logger.warning("密码修改请求: 在当前会话中找不到用户，重定向到登录")
        return RedirectResponse(url="/login", status_code=303)
    
    logger.info(f"密码修改请求: 用户 {user.username} (ID: {user.id}) 尝试修改密码")
    
    # 验证当前密码
    current_password_valid = User.verify_password(current_password, user.password_hash, user.salt)
    logger.info(f"密码修改请求: 当前密码验证结果: {current_password_valid}")
    
    if not current_password_valid:
        logger.warning(f"密码修改请求: 用户 {user.username} 当前密码验证失败")
        return RedirectResponse(
            url="/user/settings?error=当前密码错误",
            status_code=303
        )
    
    # 验证新密码
    if new_password != confirm_password:
        logger.warning(f"密码修改请求: 用户 {user.username} 两次输入的新密码不一致")
        return RedirectResponse(
            url="/user/settings?error=两次输入的新密码不一致",
            status_code=303
        )
    
    if len(new_password) < 6:
        logger.warning(f"密码修改请求: 用户 {user.username} 新密码长度不足6个字符")
        return RedirectResponse(
            url="/user/settings?error=新密码长度不能少于6个字符",
            status_code=303
        )
    
    # 记录修改前的密码哈希信息（用于调试）
    logger.info(f"密码修改请求: 修改前的密码哈希: {user.password_hash}")
    logger.info(f"密码修改请求: 修改前的盐值: {user.salt}")
    
    # 更新密码
    password_hash, salt = User.hash_password(new_password)
    user.password_hash = password_hash
    user.salt = salt
    user.updated_at = datetime.now()
    
    # 更新会话时长
    session_duration = getattr(request.form, "session_duration", None)
    if session_duration:
        try:
            user.session_duration = int(session_duration)
            logger.info(f"密码修改请求: 更新会话时长为 {user.session_duration} 分钟")
        except ValueError:
            logger.warning(f"密码修改请求: 无效的会话时长值: {session_duration}")
            pass
    
    db.commit()
    logger.info(f"密码修改请求: 密码已成功更新到数据库")
    
    # 验证密码是否真的更新了
    db.refresh(user)
    is_password_updated = User.verify_password(new_password, user.password_hash, user.salt)
    logger.info(f"密码修改请求: 密码更新验证结果: {is_password_updated}")
    logger.info(f"密码修改请求: 修改后的密码哈希: {user.password_hash}")
    logger.info(f"密码修改请求: 修改后的盐值: {user.salt}")
    
    # 使所有现有会话失效
    old_sessions = db.query(UserSession).filter(UserSession.user_id == user.id).all()
    logger.info(f"密码修改请求: 使 {len(old_sessions)} 个现有会话失效")
    db.query(UserSession).filter(UserSession.user_id == user.id).delete()
    db.commit()
    
    # 创建新会话
    token, expires_at = UserSession.create_session(user.id, user.session_duration)
    session = UserSession(
        user_id=user.id,
        session_token=token,
        expires_at=expires_at
    )
    db.add(session)
    db.commit()
    logger.info(f"密码修改请求: 创建了新会话，过期时间: {expires_at}")
    
    # 设置新会话Cookie
    response = RedirectResponse(
        url="/user/settings?success=true",
        status_code=303
    )
    # 格式化为HTTP cookie标准的GMT时间字符串
    expires_str = expires_at.strftime("%a, %d %b %Y %H:%M:%S GMT")
    logger.info(f"密码修改请求: 设置Cookie过期时间: {expires_str}")
    response.set_cookie(
        key="session_token",
        value=token,
        expires=expires_str,
        httponly=True,
        secure=False,  # 改为False以在开发环境中正常工作
        samesite="lax"
    )
    
    logger.info(f"===== 密码修改请求处理完成 =====")
    return response

@app.post("/user/update_session_duration")
async def update_session_duration(
    request: Request,
    session_duration: int = Form(...),
    db: Session = Depends(get_db)
):
    # 获取当前用户
    user = getattr(request.state, "user", None)
    if not user:
        return RedirectResponse(url="/login", status_code=303)
    
    # 验证会话时长
    if session_duration < 10 or session_duration > 1440:  # 10分钟到24小时
        return RedirectResponse(
            url="/user/settings?error=会话时长必须在10分钟到24小时之间",
            status_code=303
        )
    
    # 更新会话时长
    user.session_duration = session_duration
    user.updated_at = datetime.now()
    db.commit()
    
    return RedirectResponse(
        url="/user/settings?success=true",
        status_code=303
    )

# 管理员用户管理路由
@app.get("/admin/users", response_class=HTMLResponse)
async def admin_users(
    request: Request,
    error: str = None,
    success: bool = False,
    db: Session = Depends(get_db)
):
    # 获取当前用户
    user = getattr(request.state, "user", None)
    if not user or user.user_type not in ['super_admin', 'admin']:
        raise HTTPException(status_code=403, detail="无权访问此页面")
    
    # 获取所有用户
    try:
        users = db.query(User).all()
    except Exception as e:
        logger.error(f"Failed to fetch users: {str(e)}")
        return templates.TemplateResponse(
            "error.html",
            {
                "request": request,
                "title": "数据库查询错误",
                "message": "获取用户列表失败，请检查数据库连接",
                "error_details": str(e),
                "show_config_button": True
            },
            status_code=500
        )
    
    try:
        theme = Config.get_theme(db)
    except Exception:
        theme = "light"
    
    return templates.TemplateResponse(
        "admin_users.html",
        {
            "request": request,
            "users": users,
            "theme": theme,
            "error": error,
            "success": success
        }
    )

@app.get("/admin/users/add", response_class=HTMLResponse)
async def add_user_form(
    request: Request,
    error: str = None,
    db: Session = Depends(get_db)
):
    # 验证当前用户是否为管理员
    current_user = getattr(request.state, "user", None)
    if not current_user or current_user.user_type not in ['super_admin', 'admin']:
        raise HTTPException(status_code=403, detail="无权访问此页面")
    
    try:
        theme = Config.get_theme(db)
    except Exception:
        theme = "light"
    
    return templates.TemplateResponse(
        "add_user.html",
        {
            "request": request,
            "theme": theme,
            "error": error
        }
    )

@app.post("/admin/users/add")
async def add_user(
    request: Request,
    username: str = Form(...),
    password: str = Form(...),
    confirm_password: str = Form(...),
    user_type: str = Form(...),
    db: Session = Depends(get_db)
):
    # 验证当前用户是否为管理员
    current_user = getattr(request.state, "user", None)
    if not current_user or current_user.user_type not in ['super_admin', 'admin']:
        raise HTTPException(status_code=403, detail="无权执行此操作")
    
    # 验证密码
    if password != confirm_password:
        return RedirectResponse(
            url="/admin/users?error=两次输入的密码不一致",
            status_code=303
        )
    
    if len(password) < 6:
        return RedirectResponse(
            url="/admin/users?error=密码长度不能少于6个字符",
            status_code=303
        )
    
    # 检查用户名是否已存在
    existing_user = db.query(User).filter(User.username == username).first()
    if existing_user:
        return RedirectResponse(
            url="/admin/users?error=用户名已存在",
            status_code=303
        )
    
    # 创建新用户
    password_hash, salt = User.hash_password(password)
    new_user = User(
        username=username,
        password_hash=password_hash,
        salt=salt,
        is_active=True,
        user_type=user_type,
        is_admin=(user_type in ['super_admin', 'admin']),  # 兼容旧字段
        session_duration=360,  # 默认6小时
        created_at=datetime.now(),
        updated_at=datetime.now()
    )
    db.add(new_user)
    db.commit()
    
    return RedirectResponse(
        url="/admin/users?success=true",
        status_code=303
    )

@app.get("/admin/users/edit/{user_id}")
async def edit_user_page(
    request: Request,
    user_id: int,
    error: str = None,
    success: bool = False,
    db: Session = Depends(get_db)
):
    # 验证当前用户是否为管理员
    current_user = getattr(request.state, "user", None)
    if not current_user or current_user.user_type not in ['super_admin', 'admin']:
        raise HTTPException(status_code=403, detail="无权访问此页面")
    
    # 获取要编辑的用户
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        return RedirectResponse(
            url="/admin/users?error=用户不存在",
            status_code=303
        )
    
    # 不允许编辑admin用户
    if user.username.lower() == "admin":
        return RedirectResponse(
            url="/admin/users?error=超级用户admin不能被编辑",
            status_code=303
        )
    
    try:
        theme = Config.get_theme(db)
    except Exception:
        theme = "light"
    
    return templates.TemplateResponse(
        "user_edit.html",
        {
            "request": request,
            "user": user,
            "theme": theme,
            "error": error,
            "success": success
        }
    )

@app.post("/admin/users/edit/{user_id}")
async def edit_user(
    request: Request,
    user_id: int,
    username: str = Form(...),
    session_duration: int = Form(...),
    is_active: bool = Form(False),
    user_type: str = Form(...),
    db: Session = Depends(get_db)
):
    # 验证当前用户是否为管理员
    current_user = getattr(request.state, "user", None)
    if not current_user or current_user.user_type not in ['super_admin', 'admin']:
        raise HTTPException(status_code=403, detail="无权执行此操作")
    
    # 获取要编辑的用户
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        return RedirectResponse(
            url="/admin/users?error=用户不存在",
            status_code=303
        )
    
    # 检查用户名是否已存在（排除当前用户）
    existing_user = db.query(User).filter(User.username == username, User.id != user_id).first()
    if existing_user:
        return RedirectResponse(
            url="/admin/users?error=用户名已存在",
            status_code=303
        )
    
    # 检查是否是admin用户，如果是则不允许取消管理员权限
    if user.username.lower() == "admin" and not is_admin:
        return RedirectResponse(
            url="/admin/users?error=超级用户admin的管理员权限不能被取消",
            status_code=303
        )
    
    # 检查是否是admin用户，如果是则不允许禁用账户
    if user.username.lower() == "admin" and not is_active:
        return RedirectResponse(
            url="/admin/users?error=超级用户admin的账户不能被禁用",
            status_code=303
        )
    
    # 更新用户信息
    user.username = username
    user.session_duration = session_duration
    user.is_active = is_active
    user.user_type = user_type
    user.is_admin = (user_type in ['super_admin', 'admin'])  # 兼容旧字段
    user.updated_at = datetime.now()
    db.commit()
    
    return RedirectResponse(
        url="/admin/users?success=true",
        status_code=303
    )

@app.post("/admin/users/delete/{user_id}")
async def delete_user(
    request: Request,
    user_id: int,
    db: Session = Depends(get_db)
):
    # 验证当前用户是否为管理员
    current_user = getattr(request.state, "user", None)
    if not current_user or current_user.user_type not in ['super_admin', 'admin']:
        return JSONResponse(
            status_code=403,
            content={"error": "无权执行此操作"}
        )
    
    # 获取要删除的用户
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        return JSONResponse(
            status_code=404,
            content={"error": "用户不存在"}
        )
    
    # 不允许删除自己
    if user.id == current_user.id:
        return JSONResponse(
            status_code=400,
            content={"error": "不能删除当前登录的用户"}
        )
    
    # 不允许删除admin用户
    if user.username.lower() == "admin":
        return JSONResponse(
            status_code=400,
            content={"error": "超级用户admin不能被删除"}
        )
    
    # 删除用户的所有会话
    db.query(UserSession).filter(UserSession.user_id == user.id).delete()
    
    # 删除用户
    db.delete(user)
    db.commit()
    
    # 返回成功响应
    return JSONResponse(
        status_code=200,
        content={"success": True}
    )

@app.get("/admin/users/reset_password/{user_id}", response_class=HTMLResponse)
async def reset_password_page(
    request: Request,
    user_id: int,
    error: str = None,
    success: bool = False,
    db: Session = Depends(get_db)
):
    # 验证当前用户是否为管理员
    current_user = getattr(request.state, "user", None)
    if not current_user or not current_user.is_admin:
        raise HTTPException(status_code=403, detail="无权访问此页面")
    
    # 获取要重置密码的用户
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        return RedirectResponse(
            url="/admin/users?error=用户不存在",
            status_code=303
        )
    
    # 获取主题配置
    try:
        theme = Config.get_theme(db)
    except Exception:
        theme = "light"
    
    return templates.TemplateResponse(
        "reset_password.html",
        {
            "request": request,
            "user": user,
            "error": error,
            "success": success,
            "theme": theme
        }
    )

@app.post("/admin/users/reset_password/{user_id}")
async def reset_password(
    request: Request,
    user_id: int,
    new_password: str = Form(...),
    confirm_password: str = Form(...),
    db: Session = Depends(get_db)
):
    # 验证当前用户是否为管理员
    current_user = getattr(request.state, "user", None)
    if not current_user or not current_user.is_admin:
        raise HTTPException(status_code=403, detail="无权执行此操作")
    
    # 验证密码
    if new_password != confirm_password:
        return RedirectResponse(
            url="/admin/users?error=两次输入的密码不一致",
            status_code=303
        )
    
    if len(new_password) < 6:
        return RedirectResponse(
            url="/admin/users?error=密码长度不能少于6个字符",
            status_code=303
        )
    
    # 获取要重置密码的用户
    user = db.query(User).filter(User.id == user_id).first()
    if not user:
        return RedirectResponse(
            url="/admin/users?error=用户不存在",
            status_code=303
        )
    
    # 更新密码
    password_hash, salt = User.hash_password(new_password)
    user.password_hash = password_hash
    user.salt = salt
    user.updated_at = datetime.now()
    
    # 使所有现有会话失效
    db.query(UserSession).filter(UserSession.user_id == user.id).delete()
    
    db.commit()
    
    return RedirectResponse(
        url="/admin/users?success=true",
        status_code=303
    )

# 数据库配置路由
@app.get("/database_config", response_class=HTMLResponse)
async def database_config(
    request: Request, 
    error: str = None,
    message: str = None,
    success: bool = False,
    db: Session = Depends(get_db)
):
    # 直接从cookie中获取session_token并验证用户是否为管理员
    session_token = request.cookies.get("session_token")
    if not session_token:
        logger.warning("Unauthorized access to database config: no session token")
        raise HTTPException(status_code=403, detail="管理员权限才能访问此页面")
    
    # 查询会话
    session = db.query(UserSession).filter(
        UserSession.session_token == session_token,
        UserSession.expires_at > datetime.utcnow()
    ).first()
    
    if not session:
        logger.warning(f"Unauthorized access to database config: invalid session token")
        raise HTTPException(status_code=403, detail="管理员权限才能访问此页面")
    
    # 获取用户
    user = db.query(User).filter(User.id == session.user_id, User.is_active == True).first()
    if not user or user.user_type not in ['super_admin', 'admin']:
        logger.warning(f"Unauthorized access to database config by non-admin user: {user.username if user else 'unknown'}")
        raise HTTPException(status_code=403, detail="管理员权限才能访问此页面")
    try:
        theme = Config.get_theme(db)
    except Exception:
        # 如果数据库连接失败，使用默认主题
        theme = "light"
    
    # 准备模板上下文
    context = {
        "request": request, 
        "config": DB_CONFIG, 
        "theme": theme,
        "error": error,
        "message": message,
        "success": success,
        "connection_error": DB_CONFIG.get("connection_error")
    }
    
    return templates.TemplateResponse("database_config.html", context)

@app.get("/test_route")
async def test_route(request: Request):
    user = getattr(request.state, 'user', None)
    user_info = f"User: {user.username if user else 'Not logged in'}"
    is_admin = getattr(request.state, 'is_admin', False)
    session_token = request.cookies.get('session_token', 'No token')
    return HTMLResponse(f"<h1>Test Route Working</h1><p>{user_info}</p><p>Is Admin: {is_admin}</p><p>Session Token: {session_token}</p>")

@app.get("/debug_permissions")
async def debug_permissions(request: Request):
    """调试路由：检查用户权限状态"""
    user = getattr(request.state, 'user', None)
    is_admin = getattr(request.state, 'is_admin', False)
    
    # 模拟数据库配置页面的权限检查
    try:
        if not is_admin:
            raise HTTPException(status_code=403, detail="管理员权限才能访问此页面")
        permission_check = "通过" 
    except HTTPException as e:
        permission_check = f"失败 (状态码: {e.status_code})"
    
    return HTMLResponse(
        f"""
        <h1>权限调试信息</h1>
        <p>用户: {user.username if user else '未登录'}</p>
        <p>是否管理员: {is_admin}</p>
        <p>权限检查结果: {permission_check}</p>
        <p>请求路径: {request.url.path}</p>
        """
    )

@app.post("/database_config")
async def update_database_config(
    request: Request,
    db_type: str = Form(...),
    sqlite_path: Optional[str] = Form(None),
    mysql_host: Optional[str] = Form(None),
    mysql_port: Optional[int] = Form(None),
    mysql_user: Optional[str] = Form(None),
    mysql_password: Optional[str] = Form(None),
    mysql_db_name: Optional[str] = Form(None),
    theme: str = Form(...),
    db: Session = Depends(get_db)
):
    # 直接从cookie中获取session_token并验证用户是否为管理员
    session_token = request.cookies.get("session_token")
    if not session_token:
        logger.warning("Unauthorized attempt to update database config: no session token")
        raise HTTPException(status_code=403, detail="管理员权限才能修改数据库配置")
    
    # 查询会话
    session = db.query(UserSession).filter(
        UserSession.session_token == session_token,
        UserSession.expires_at > datetime.utcnow()
    ).first()
    
    if not session:
        logger.warning(f"Unauthorized attempt to update database config: invalid session token")
        raise HTTPException(status_code=403, detail="管理员权限才能修改数据库配置")
    
    # 获取用户
    user = db.query(User).filter(User.id == session.user_id, User.is_active == True).first()
    if not user or user.user_type not in ['super_admin', 'admin']:
        logger.warning(f"Unauthorized attempt to update database config by non-admin user: {user.username if user else 'unknown'}")
        raise HTTPException(status_code=403, detail="管理员权限才能修改数据库配置")
    # 如果密码字段为空且之前有密码，保留原密码
    if (mysql_password == "" or mysql_password is None) and DB_CONFIG.get("mysql_password"):
        logger.debug("Password field is empty, keeping original password")
        mysql_password = DB_CONFIG.get("mysql_password")
    
    # 检查密码是否被截断（例如，如果原密码是 alibaba999 但收到的是 alibaba）
    original_password = DB_CONFIG.get("mysql_password", "")
    if mysql_password and original_password and mysql_password != original_password and original_password.startswith(mysql_password):
        logger.debug(f"Password appears to be truncated. Original: {len(original_password)} chars, Received: {len(db_password)} chars")
        logger.debug(f"Using original password instead")
        mysql_password = original_password
    
    new_config = {
    "db_type": db_type,
    "sqlite_path": sqlite_path,
    "mysql_host": mysql_host,
    "mysql_port": mysql_port,
    "mysql_user": mysql_user,
    "mysql_password": mysql_password,
    "mysql_db_name": mysql_db_name
    }
    
    # 记录密码长度和内容，帮助调试
    logger.debug(f"Password length: {len(mysql_password) if mysql_password else 0}")
    logger.debug(f"Password value: {mysql_password}")
    
    # 保存配置到文件
    with open(CONFIG_FILE, "w") as f:
        # 使用缩进和分隔符参数美化JSON格式
        json.dump(new_config, f, ensure_ascii=False, indent=4, separators=(',', ': '))
    
    # 清除之前的连接错误状态
    if "connection_error" in DB_CONFIG:
        del DB_CONFIG["connection_error"]
    
    # 更新全局配置
    DB_CONFIG.update(new_config)
    
    # 更新主题配置
    Config.set_theme(db, theme)
    
    # 保存配置后直接返回成功页面，不进行连接测试
    response = RedirectResponse(url="/database_config?success=true", status_code=303)
    response.set_cookie("session_refreshed", "1", httponly=True)
    # 彻底清理所有可能的数据库配置 Cookie，增加更多保护
    response.delete_cookie("db_path")
    response.delete_cookie("db_name")
    response.delete_cookie("db_password")
    response.delete_cookie("db_config_leak")
    response.delete_cookie("db_type")
    response.delete_cookie("db_host")
    response.delete_cookie("db_port")
    response.delete_cookie("db_user")
    return response

# 根路由 - 显示首页
@app.get("/")
async def root(request: Request):
    return templates.TemplateResponse("index.html", {"request": request, "user": getattr(request.state, 'user', None)})

@app.get("/switch_to_sqlite")
async def switch_to_sqlite(request: Request, db: Session = Depends(get_db)):
    """切换到SQLite数据库"""
    # 直接从cookie中获取session_token并验证用户是否为管理员
    session_token = request.cookies.get("session_token")
    if not session_token:
        logger.warning("Unauthorized attempt to switch to SQLite: no session token")
        raise HTTPException(status_code=403, detail="管理员权限才能切换数据库类型")
    
    # 查询会话
    session = db.query(UserSession).filter(
        UserSession.session_token == session_token,
        UserSession.expires_at > datetime.utcnow()
    ).first()
    
    if not session:
        logger.warning(f"Unauthorized attempt to switch to SQLite: invalid session token")
        raise HTTPException(status_code=403, detail="管理员权限才能切换数据库类型")
    
    # 获取用户
    user = db.query(User).filter(User.id == session.user_id, User.is_active == True).first()
    if not user or user.user_type not in ['super_admin', 'admin']:
        logger.warning(f"Unauthorized attempt to switch to SQLite by non-admin user: {user.username if user else 'unknown'}")
        raise HTTPException(status_code=403, detail="管理员权限才能切换数据库类型")
    # 更新配置
    new_config = {
        "db_type": "sqlite",
        "db_path": "tqsdk.db",
        "db_host": "",
        "db_port": "",
        "db_user": "",
        "db_password": "",
        "db_name": ""
    }
    
    # 保存配置到文件
    with open(CONFIG_FILE, "w") as f:
        json.dump(new_config, f, ensure_ascii=False)
    
    # 更新全局配置
    DB_CONFIG.update(new_config)
    
    # 重新创建数据库连接
    global engine, SessionLocal
    engine = get_db_engine()
    SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
    
    # 重定向到数据库配置页面
    return RedirectResponse(url="/database_config?success=true", status_code=303)

@app.get("/")
async def root(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

# 监控产品页面
@app.get("/monitored_products", response_class=HTMLResponse)
async def get_monitored_products(
    request: Request,
    db: Session = Depends(get_db)
):
    # 查询所有监控中的产品
    monitored_products = db.query(Product).filter(Product.is_monitored == 1).all()
    return templates.TemplateResponse("monitored_products.html", {"request": request, "monitored_products": monitored_products})

# 产品页面
@app.get("/products", response_class=HTMLResponse)
async def get_products(
    request: Request,
    sort: Optional[str] = None,
    order: Optional[str] = None,
    db: Session = Depends(get_db)
):
    query = db.query(Product)
    
    # 处理排序
    if sort and order:
        if order == "asc":
            query = query.order_by(getattr(Product, sort).asc())
        else:
            query = query.order_by(getattr(Product, sort).desc())
    
    products = query.all()
    return templates.TemplateResponse("products.html", {"request": request, "products": products})

# 添加产品页面
@app.get("/products/add", response_class=HTMLResponse)
async def add_product_form(request: Request):
    # 验证当前用户权限
    current_user = getattr(request.state, "user", None)
    if not current_user or current_user.user_type not in ['super_admin', 'admin', 'operator']:
        raise HTTPException(status_code=403, detail="无权访问此页面")
    
    return templates.TemplateResponse("add_product.html", {"request": request})

# 添加产品处理
@app.post("/products/add")
async def add_product(
    request: Request,
    name: str = Form(...),
    exchange_code: str = Form(...),
    margin: float = Form(...),
    stoploss: float = Form(...),
    takeprofit: float = Form(...),
    limit_stoploss: float = Form(...),
    mtp: str = Form(...),
    dks: str = Form(...),
    is_monitored: int = Form(...),
    db: Session = Depends(get_db)
):
    # 验证当前用户权限
    current_user = getattr(request.state, "user", None)
    if not current_user or current_user.user_type not in ['super_admin', 'admin', 'operator']:
        raise HTTPException(status_code=403, detail="无权执行此操作")
    new_product = Product(
        name=name,
        exchange_code=exchange_code,
        margin=margin,
        stoploss=stoploss,
        takeprofit=takeprofit,
        limit_stoploss=limit_stoploss,
        mtp=mtp,
        dks=dks,
        is_monitored=is_monitored,
        add_time=datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    )
    db.add(new_product)
    db.commit()
    
    # 根据来源参数决定重定向
    from_param = request.query_params.get("from")
    if from_param == "monitored":
        return RedirectResponse(url="/monitored_products", status_code=303)
    else:
        return RedirectResponse(url="/products", status_code=303)

# 编辑产品页面
@app.get("/products/{product_id}/edit", response_class=HTMLResponse)
async def edit_product_form(request: Request, product_id: int, db: Session = Depends(get_db)):
    # 验证当前用户权限
    current_user = getattr(request.state, "user", None)
    if not current_user or current_user.user_type not in ['super_admin', 'admin', 'operator']:
        raise HTTPException(status_code=403, detail="无权访问此页面")
    
    product = db.query(Product).filter(Product.id == product_id).first()
    if not product:
        raise HTTPException(status_code=404, detail="Product not found")
    return templates.TemplateResponse("edit_product.html", {"request": request, "product": product})

# 更新产品处理
@app.post("/products/{product_id}/edit")
async def update_product(
    request: Request,
    product_id: int,
    name: str = Form(...),
    exchange_code: str = Form(...),
    margin: float = Form(...),
    stoploss: float = Form(...),
    takeprofit: float = Form(...),
    limit_stoploss: float = Form(...),
    mtp: str = Form(...),
    dks: str = Form(...),
    is_monitored: int = Form(...),
    db: Session = Depends(get_db)
):
    # 验证当前用户权限
    current_user = getattr(request.state, "user", None)
    if not current_user or current_user.user_type not in ['super_admin', 'admin', 'operator']:
        raise HTTPException(status_code=403, detail="无权执行此操作")
    product = db.query(Product).filter(Product.id == product_id).first()
    if not product:
        raise HTTPException(status_code=404, detail="Product not found")
    
    product.name = name
    product.exchange_code = exchange_code
    product.margin = margin
    product.stoploss = stoploss
    product.takeprofit = takeprofit
    product.limit_stoploss = limit_stoploss
    product.mtp = mtp
    product.dks = dks
    product.is_monitored = is_monitored
    
    db.commit()
    
    # 根据来源参数决定重定向
    from_param = request.query_params.get("from")
    if from_param == "monitored":
        return RedirectResponse(url="/monitored_products", status_code=303)
    else:
        return RedirectResponse(url="/products", status_code=303)

# 删除产品
@app.get("/products/{product_id}/delete")
async def delete_product(request: Request, product_id: int, db: Session = Depends(get_db)):
    # 验证当前用户权限
    current_user = getattr(request.state, "user", None)
    if not current_user or current_user.user_type not in ['super_admin', 'admin']:
        raise HTTPException(status_code=403, detail="无权执行此操作")
    
    product = db.query(Product).filter(Product.id == product_id).first()
    if not product:
        raise HTTPException(status_code=404, detail="Product not found")
    
    db.delete(product)
    db.commit()
    
    # 检查请求的来源
    referer = request.headers.get("referer", "")
    if "monitored_products" in referer:
        return RedirectResponse(url="/monitored_products")
    else:
        return RedirectResponse(url="/products")

# 配置页面
@app.get("/config", response_class=HTMLResponse)
async def get_config(request: Request, db: Session = Depends(get_db)):
    configs = db.query(Config).all()
    return templates.TemplateResponse("config.html", {"request": request, "configs": configs})

# 编辑配置页面
@app.get("/config/{config_id}/edit", response_class=HTMLResponse)
async def edit_config_form(request: Request, config_id: int, db: Session = Depends(get_db)):
    # 验证当前用户权限
    current_user = getattr(request.state, "user", None)
    if not current_user or current_user.user_type not in ['super_admin', 'admin']:
        raise HTTPException(status_code=403, detail="无权访问此页面")
    
    config = db.query(Config).filter(Config.id == config_id).first()
    if not config:
        raise HTTPException(status_code=404, detail="Config not found")
    return templates.TemplateResponse("edit_config.html", {"request": request, "config": config})

# 更新配置处理
@app.post("/config/{config_id}/edit")
async def update_config(
    request: Request,
    config_id: int,
    value: str = Form(...),
    db: Session = Depends(get_db)
):
    # 验证当前用户权限
    current_user = getattr(request.state, "user", None)
    if not current_user or current_user.user_type not in ['super_admin', 'admin']:
        raise HTTPException(status_code=403, detail="无权执行此操作")
    config = db.query(Config).filter(Config.id == config_id).first()
    if not config:
        raise HTTPException(status_code=404, detail="Config not found")
    
    config.value = value
    config.updated_at = datetime.now()
    
    db.commit()
    return RedirectResponse(url="/config", status_code=303)

# 交易记录页面
@app.get("/trade_records", response_class=HTMLResponse)
async def get_trade_records(
    request: Request, 
    page: int = 1, 
    limit: int = 20,
    product_name: Optional[str] = None,
    direction: Optional[str] = None,
    offset: Optional[str] = None,
    db: Session = Depends(get_db)
):
    query = db.query(TradeRecord)
    
    # 应用过滤条件
    if product_name:
        query = query.filter(TradeRecord.product_name.like(f"%{product_name}%"))
    if direction:
        query = query.filter(TradeRecord.direction == direction)
    if offset:
        query = query.filter(TradeRecord.offset == offset)
    
    # 计算总记录数和总页数
    total_records = query.count()
    total_pages = (total_records + limit - 1) // limit
    
    # 分页
    records = query.order_by(TradeRecord.id.desc()).offset((page - 1) * limit).limit(limit).all()
    
    return templates.TemplateResponse(
        "trade_records.html", 
        {
            "request": request, 
            "records": records,
            "page": page,
            "limit": limit,
            "total_pages": total_pages,
            "product_name": product_name,
            "direction": direction,
            "offset": offset
        }
    )

# 监控中品种管理
@app.get("/monitored_products", response_class=HTMLResponse)
async def monitored_products(request: Request, db: Session = Depends(get_db)):
    # 查询所有监控中的品种
    products = db.query(Product).filter(Product.is_monitored == True).all()
    return templates.TemplateResponse("monitored_products.html", {"request": request, "products": products})

# 监控中品种编辑页面
@app.get("/monitored_products/{product_id}/edit", response_class=HTMLResponse)
async def edit_monitored_product_form(request: Request, product_id: int, db: Session = Depends(get_db)):
    # 验证当前用户权限
    current_user = getattr(request.state, "user", None)
    if not current_user or current_user.user_type not in ['super_admin', 'admin', 'operator']:
        raise HTTPException(status_code=403, detail="无权访问此页面")
    
    product = db.query(Product).filter(Product.id == product_id).first()
    if not product:
        raise HTTPException(status_code=404, detail="Product not found")
    return templates.TemplateResponse("edit_product.html", {"request": request, "product": product})

# 监控中品种更新处理
@app.post("/monitored_products/{product_id}/edit")
async def update_monitored_product(
    request: Request,
    product_id: int,
    name: str = Form(...),
    exchange_code: str = Form(...),
    margin: float = Form(...),
    stoploss: float = Form(...),
    takeprofit: float = Form(...),
    limit_stoploss: float = Form(...),
    mtp: str = Form(...),
    dks: str = Form(...),
    is_monitored: int = Form(...),
    db: Session = Depends(get_db)
):
    # 验证当前用户权限
    current_user = getattr(request.state, "user", None)
    if not current_user or current_user.user_type not in ['super_admin', 'admin', 'operator']:
        raise HTTPException(status_code=403, detail="无权执行此操作")
    product = db.query(Product).filter(Product.id == product_id).first()
    if not product:
        raise HTTPException(status_code=404, detail="Product not found")
    
    product.name = name
    product.exchange_code = exchange_code
    product.margin = margin
    product.stoploss = stoploss
    product.takeprofit = takeprofit
    product.limit_stoploss = limit_stoploss
    product.mtp = mtp
    product.dks = dks
    product.is_monitored = is_monitored
    
    db.commit()
    return RedirectResponse(url="/monitored_products", status_code=303)

# 监控中品种删除
@app.get("/monitored_products/{product_id}/delete")
async def delete_monitored_product(request: Request, product_id: int, db: Session = Depends(get_db)):
    # 验证当前用户权限
    current_user = getattr(request.state, "user", None)
    if not current_user or current_user.user_type not in ['super_admin', 'admin']:
        raise HTTPException(status_code=403, detail="无权执行此操作")
    
    product = db.query(Product).filter(Product.id == product_id).first()
    if not product:
        raise HTTPException(status_code=404, detail="Product not found")
    
    db.delete(product)
    db.commit()
    return RedirectResponse(url="/monitored_products", status_code=303)

# 帮助中心
@app.get("/help", response_class=HTMLResponse)
async def help_center(request: Request):
    return templates.TemplateResponse("help.html", {"request": request})

# 导入数据库配置向导路由
try:
    from db_config_routes import router
    app.include_router(router)
    print(f"Added {len(router.routes)} routes from db_config_routes")
    print("db_config_routes included successfully")
except Exception as e:
    print(f"Failed to import db_config_routes: {e}")    
    # 注册路由           
    logger.info("数据库配置向导路由已加载")

    
    
    


# 启动应用
if __name__ == "__main__":
    import uvicorn
    
    # 确保目录存在
    os.makedirs("static", exist_ok=True)
    os.makedirs("templates", exist_ok=True)
    
    # 启动服务器 - 使用正确的方式启动uvicorn
    uvicorn.run("__main__:app", host="127.0.0.1", port=8080)
